package com

import com.core.Generation
import com.sun.nio.file.ExtendedWatchEventModifier
import com.utils.ModuleUtils
import org.slf4j.MarkerFactory
import java.io.File
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.concurrent.thread
import kotlin.io.path.*
import kotlin.system.exitProcess

object WatchFileService {

    /**
     * 文件夹监控服务
     */
    private lateinit var watchService: WatchService
    private lateinit var executorService: ExecutorService
    private var watchDirectorys = ConcurrentHashMap<String, WatchFile>()

    private var isLoop: Boolean = false

    val WATCH_NAME = MarkerFactory.getMarker("WATCH")

    var watchConfigs: MutableList<FileInfo> = mutableListOf()

    /**
     * 初始化文件监控服务，包括加载配置、注册监听器。
     */
    fun init() {
        // 初始化线程池和监控服务
        executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2) // 使用固定大小的线程池
        watchService = FileSystems.getDefault().newWatchService()
        // 加载配置文件
        loadConfig()
        startLoopTask()
        // 开始监控
        startWatch()
    }

    /**
     * 加载配置信息，并初始化监控。
     */
    private fun loadConfig() {
        ModuleUtils.onLoadConfigBefore()
        // 清空旧配置
        watchConfigs.clear()

        val configPath = Utils.configPath()
        if (configPath?.exists() == true) {
            logger.info("读取配置路径：${configPath.pathString}")
            watchConfigs = JSON.decodeFromString(configPath.readText())
        }

        // 若无配置，则使用默认配置
        if (watchConfigs.isEmpty()) {
            watchConfigs.add(
                FileInfo(
                    "resConfig", "xml", Path("./"), mutableSetOf(), mutableListOf()
                )
            )
            logger.info("不存在配置，使用默认配置，监听当前目录")
        }

        // 验证配置是否合法
        val verify = watchConfigs.any {
            it.cmpressPng.enable && (it.cmpressPng.suffix.isBlank() || it.cmpressPng.quality.isBlank())
        }
        if (verify) {
            alert("配置不合格")
            exitProcess(0)
        }
        logger.debug("读取到配置文件：$watchConfigs")

        // 初始化监控目录
        watchConfigs.forEach { fileInfo ->
            // 默认将监控目录添加到跟路径
            fileInfo.localRootPath.add(fileInfo.watchFile.pathString)

            // 替换配置的路径为统一的 \\
            fileInfo.localRootPath.replaceAll {
                var path = it.replace("/", "\\")
                if (!path.endsWith("\\")) path += "\\"
                path
            }
            fileInfo.outFilePath = Path(fileInfo.outPath.pathString, fileInfo.fileName())

            fileInfo.excludes.add(fileInfo.fileName())

            initReg(fileInfo)
        }

        toast("文件监控已初始化完成")
        logger.info("监控初始化完成")
        ModuleUtils.onLoadConfigAfter()
    }

    /**
     * 重启文件监控服务，重新加载配置并开始监控。
     */
    fun rebootWatch() {
        logger.info("执行重启监控")
        stopWatch()
        loadConfig()
        startLoopTask()
        startWatch()
    }

    /**
     * 停止文件监控服务。
     */
    fun stopWatch() {
        logger.info("停止服务")
        isLoop = false
        // 取消所有监控键
        watchDirectorys.forEach { (_, u) ->
            u.watchKey?.cancel()
        }
        // 清空目录映射
        watchDirectorys.clear()
    }

    /**
     * key:监控路径
     * value:发生的事件
     */
    private val cacheWatchFile = ConcurrentHashMap<String, MutableList<WatchEvent<*>>>()

    /**
     * 启动事件轮询
     */
    fun startLoopTask() {
        thread(isDaemon = true, name = "watch-thread") {
            try {
                while (true) {
                    // 获取并处理事件
                    val key = watchService.take()
                    val str = key.watchable().toString()
                    for (event in key.pollEvents()) {
                        val kind = event.kind()
                        val events: Any? = event.context()

                        logger.debug("监听到事件： 类型：${kind.name()} 事件：$events 监控路径：$str")
                        if (events == null) {
                            logger.info("本次监听变动文件是null: 类型: ${kind.name()} 监控路径: $str")
                            continue
                        }
                        if (events !is Path) {
                            logger.info("本次监听变动文件类型不是Path: 类型: ${kind.name()} 监控路径: $str 变动文件: $events")
                            continue
                        }
                        cacheWatchFile.getOrPut(str) {
                            mutableListOf()
                        }.add(event)
                    }
                    // 重置键，准备接收下一批事件
                    if (!key.reset()) {
                        logger.warn("Key has been unregistered and will no longer be watched. file: $str")
                    }
                    Thread.sleep(100)
                }
            } catch (e: Exception) {
                logger.error("Error watching directory: ${e.message}", e)
                exitProcess(1) // 优雅地退出
            }
        }

    }

    /**
     * key:监控路径
     * value:发生的事件
     */
    val pendingFile = ConcurrentHashMap<String, MutableList<WatchEvent<*>>>()

    /**
     * key:文件完整路径path
     * value:文件发生的事件
     */
    val pendingUpdateEvents = ConcurrentHashMap<String, WatchEvent<*>>()

    /**
     * 开始文件监控服务。
     */
    fun startWatch() {
        this.isLoop = true
        try {
            while (isLoop) {
                if (cacheWatchFile.isNotEmpty()) {
                    pendingFile.clear()
                    pendingUpdateEvents.clear()
                    cacheWatchFile.drainTo(pendingFile)

                    for ((key, value) in pendingFile) {
                        // 收集事件时去重
                        value.forEach { event ->
                            val context = event.context() as Path
                            val fullPath = key + File.separator + context.toString()
                            pendingUpdateEvents[fullPath] = event // 自动覆盖之前的事件
                        }
                    }
                    for ((key, event) in pendingUpdateEvents) {
                        val kind = event.kind()
                        // 完整路径
                        val affectedFilePath = Path(key)
                        val watchFile = watchDirectorys.values.find {
                            key.startsWith(it.path.pathString)
                        }
                        logger.debug("{} {} {}", kind.name(), key, watchFile)
                        if (watchFile == null) {
                            logger.info("没有找到执行器: 类型: ${kind.name()} 监控路径: $key 变动文件: $affectedFilePath")
                            continue
                        }
                        val generation = watchFile.generation
                        when (kind) {
                            StandardWatchEventKinds.ENTRY_CREATE -> {
                                if (affectedFilePath.isDirectory()) {
                                    walkFile(affectedFilePath, generation)
                                } else executorService.submit {
                                    generation.watchAddOrUpdate(affectedFilePath)
                                }
                            }

                            StandardWatchEventKinds.ENTRY_MODIFY -> {
                                if (affectedFilePath.isDirectory()) {
                                    // 非window电脑从新处理
                                    if (!Utils.onWindow() && !generation.hasAttByUrl(affectedFilePath)) {
                                        // 不存在此目录  代表是新增或者恢复的文件夹
                                        logger.info("不存在此目录  $affectedFilePath")
                                        executorService.submit {
                                            generation.allFiles(affectedFilePath) {
                                                generation.watchAddOrUpdate(it)
                                            }
                                            walkFile(affectedFilePath, generation)
                                        }
                                    }
                                } else {
                                    executorService.submit {
                                        generation.watchAddOrUpdate(affectedFilePath)
                                    }
                                }
                            }

                            StandardWatchEventKinds.ENTRY_DELETE -> {
                                if (affectedFilePath.extension.isBlank()) {
                                    // 由于文件已经删除 无法在判断是否是文件夹
                                    removeWatch(affectedFilePath)
                                }
                                if (affectedFilePath.pathString.endsWith(watchFile.generation.fileInfo.fileName())) {
                                    // 如果是保存的文件被删除  表示要重新扫描生成整个文件
                                    logger.info("缓存文件被删除 从新扫描所有文件 $affectedFilePath")
                                    watchFile.generation.clear()
                                    watchFile.generation.verify()
                                    toast("重构文件 $affectedFilePath 完成")
                                    continue
                                }
                                executorService.submit {
                                    generation.watchDelete(affectedFilePath)
                                }

                            }
                        }
                    }

                }
                Thread.sleep(1000)
            }
        } catch (e: Exception) {
            logger.error("Error watching directory: ${e.message}", e)
            exitProcess(1) // 优雅地退出
        } finally {
            executorService.shutdown() // 关闭线程池
        }
    }

    /**
     * 初始化指定文件的监控注册。
     */
    private fun initReg(fileInfo: FileInfo) {
        val generation = GenerationXml(fileInfo)
        if (!fileInfo.outFilePath.exists()) {
            generation.createNow()
        } else generation.loadFile()

        logger.info("初始化监控路径 ${fileInfo.watchFile}")

        generation.verify()

        logger.info(WATCH_NAME, "监控文件 ${fileInfo.watchFile.pathString}")

        walkFile(fileInfo.watchFile, generation)

    }

    /**
     * 遍历指定目录下的文件和子目录，注册监听器。
     */
    private fun walkFile(watchFile: Path, generation: Generation) {
        register(watchFile, generation)
        if (!Utils.onWindow()) {
            // 非window 需要给所有的子目录也注册监听
            logger.debug(WATCH_NAME, "扫描所有子目录注册监听 ${watchFile.pathString}")
            Files.walkFileTree(watchFile, object : SimpleFileVisitor<Path>() {
                override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
                    register(dir, generation)
                    return FileVisitResult.CONTINUE
                }
            })
        }
    }

    /**
     * 注册指定路径以进行文件监控
     *
     * 此函数负责为给定的路径注册一个 WatchKey，以便监控该路径下的文件变化事件
     * 如果路径不存在，或者已经存在监控，则不会进行注册
     * 对于 Windows 系统，会进行特殊处理，以避免子目录的重复注册
     *
     * @param watchPath 要监控的路径对象
     * @param generation 监控的代号，用于区分不同的监控需求
     * @return 成功注册的 WatchKey，或者如果注册失败则返回 null
     */
    private fun register(watchPath: Path, generation: Generation): WatchKey? {
        val path = watchPath.pathString
        // 检查路径是否存在，如果不存在则记录日志并返回 null
        if (!watchPath.exists()) {
            logger.info(WATCH_NAME, "监控目录不存在，无法添加监听 $path")
            return null
        }

        // 检查当前路径是否已经注册过监控，如果是，则返回 null
        val currentEntry = watchDirectorys[path]
        if (currentEntry != null && currentEntry.watchKey != null) {
            logger.info(WATCH_NAME, "监控目录已存在监听，不用新注册 $path")
            return null
        }

        // Windows 特殊处理：防止子目录重复注册
        if (Utils.onWindow()) {
            val contains = watchDirectorys.keys.find { key ->
                path.equals(key, ignoreCase = true) || path.startsWith("$key", ignoreCase = true)
            }
            if (!contains.isNullOrBlank()) {
                logger.info(WATCH_NAME, "监控目录已在被[$contains]监听，不用单独注册 $path")
                return null
            }//
        }

        // 注册 WatchKey 并更新 map
        return try {
            val watchKey = if (Utils.onWindow()) {
                watchPath.register(
                    watchService,
                    arrayOf(
                        StandardWatchEventKinds.ENTRY_CREATE,
                        StandardWatchEventKinds.ENTRY_MODIFY,
                        StandardWatchEventKinds.ENTRY_DELETE
                    ),
                    ExtendedWatchEventModifier.FILE_TREE
                )
            } else {
                watchPath.register(
                    watchService,
                    StandardWatchEventKinds.ENTRY_CREATE,
                    StandardWatchEventKinds.ENTRY_MODIFY,
                    StandardWatchEventKinds.ENTRY_DELETE
                )
            }

            logger.info(WATCH_NAME, "添加监控目录 $path")
            watchDirectorys[path] = WatchFile(watchKey, watchPath, generation)
            watchKey
        } catch (e: Exception) {
            logger.error(WATCH_NAME, "注册监控目录失败: $path", e)
            null
        }
    }


    /**
     * 只会执行取消监听操作，但不会进行缓存删除。
     *
     * @param watchPath 要取消监听的路径
     */
    private fun removeWatch(watchPath: Path) {
        logger.info(WATCH_NAME, "移除监控目录 ${watchPath.pathString}")
        watchDirectorys[watchPath.pathString]?.also {
            it.watchKey?.cancel()
            it.watchKey = null
        }
    }

}